//
//  Color.cpp
//  Prism
//
//  Created by Bas du Pre on 25-05-11.
//  Copyright 2011 UU. All rights reserved.
//

#include <cmath>
#include "graphics/Color.h"
#include "functions.h"

using namespace Prism;
using namespace Prism::Graphics;

Color::Color() :
_red(0),
_green(0),
_blue(0),
_alpha(255),
_noChangeEvent(false)
{
    
}

Color::Color(PInt red, PInt green, PInt blue, PInt alpha) :
_red(red),
_green(green),
_blue(blue),
_alpha(alpha),
_noChangeEvent(false)
{
    PRISM_ASSERT_FIX(red >= 0 && red <= 255, _red = 0, "Red should be 0-255, given value: %d", red);
    PRISM_ASSERT_FIX(green >= 0 && green <= 255, _green = 0, "Green should be 0-255, given value: %d", green);
    PRISM_ASSERT_FIX(blue >= 0 && blue <= 255, _blue = 0, "Blue should be 0-255, given value: %d", blue);
    PRISM_ASSERT_FIX(alpha >= 0 && alpha <= 255, _alpha = 255, "Alpha should be 0-255, given value: %d", alpha);
}

Color::Color(const Color & color) : 
_red(color._red),
_green(color._green),
_blue(color._blue),
_alpha(color._alpha),
_noChangeEvent(false)
{
    
}

Color::~Color() {
    
}

void Color::copy(const Color &color) {
    if (color == *this) return;
    _red = color._red;
    _green = color._green;
    _blue = color._blue;
    _alpha = color._alpha;
    EventArgs e; 
    onChange(this, &e);
}

PInt Color::red() { return _red; }
void Color::setRed(PInt red) { 
    PRISM_ASSERT_FIX(red >= 0 && red <= 255, red = 0, "Red should be 0-255, given value: %d", red);
    _red = red; 
    if (!_noChangeEvent) { 
        EventArgs e; 
        onChange(this, &e); 
    } 
}

PInt Color::green() { return _green; }
void Color::setGreen(PInt green) { 
    PRISM_ASSERT_FIX(green >= 0 && green <= 255, green = 0, "Green should be 0-255, given value: %d", green);
    _green = green; 
    if (!_noChangeEvent) { 
        EventArgs e; 
        onChange(this, &e); 
    } 
}

PInt Color::blue() { return _blue; }
void Color::setBlue(PInt blue) { 
    PRISM_ASSERT_FIX(blue >= 0 && blue <= 255, blue = 0, "Blue should be 0-255, given value: %d", blue);
    _blue = blue; 
    if (!_noChangeEvent) { 
        EventArgs e; 
        onChange(this, &e);
    } 
}

PInt Color::alpha() { return _alpha; }
void Color::setAlpha(PInt alpha) { 
    PRISM_ASSERT_FIX(alpha >= 0 && alpha <= 255, alpha = 255, "Alpha should be 0-255, given value: %d", alpha);
    _alpha = alpha; 
    if (!_noChangeEvent) { 
        EventArgs e; 
        onChange(this, &e); 
    } 
}

void Color::setRGB(PInt red, PInt green, PInt blue, PInt alpha) {
    _noChangeEvent = true;
    setRed(red);
    setGreen(green);
    setBlue(blue);
    setAlpha(alpha);
    _noChangeEvent = false;
    EventArgs e; onChange(this, &e);
}

PInt Color::hue() {
    return rgbToHsv(red(), green(), blue()).hue;
}

void Color::setHue(PInt hue) {
    HSV hsv = rgbToHsv(red(), green(), blue());
    setHSV(hue, hsv.saturation, hsv.value);
}

PInt Color::saturation() {
    return rgbToHsv(red(), green(), blue()).saturation;
}

void Color::setSaturation(PInt saturation) {
    HSV hsv = rgbToHsv(red(), green(), blue());
    setHSV(hsv.hue, saturation, hsv.value);
}

PInt Color::value() {
    return rgbToHsv(red(), green(), blue()).value;
}

void Color::setValue(PInt value) {
    HSV hsv = rgbToHsv(red(), green(), blue());
    setHSV(hsv.hue, hsv.saturation, value);
}

void Color::setHSV(PInt hue, PInt saturation, PInt value, PInt alpha) {
    PRISM_ASSERT_FIX(hue >= 0 && hue <= 255, hue = 0, "Hue should be 0-255, given value: %d", hue);
    PRISM_ASSERT_FIX(saturation >= 0 && saturation <= 255, saturation = 0, "Saturation should be 0-255, given value: %d", saturation);
    PRISM_ASSERT_FIX(value >= 0 && value <= 255, value = 0, "Value should be 0-255, given value: %d", value);
    PRISM_ASSERT_FIX(alpha >= 0 && alpha <= 255, _alpha = 255, "Alpha should be 0-255, given value: %d", alpha);
    
    _noChangeEvent = true;
    RGB rgb = hsvToRgb(hue, saturation, value);
    setRed(rgb.red);
    setGreen(rgb.green);
    setBlue(rgb.blue);
    setAlpha(alpha);
    _noChangeEvent = false;
    EventArgs e; onChange(this, &e);
}

bool Color::operator !=(const Color& color) const {
    if(compareTo(color) == 0)
        return false;
    return true;
}

Color & Color::operator =(const Color& color) {
    _red = color._red;
    _green = color._green;
    _blue = color._blue;
    _alpha = color._alpha;
    EventArgs e; onChange(this, &e);
    
    return *this;
}

bool Color::operator ==(const Color& color) const {
    if(compareTo(color) == 0)
        return true;
    return false;
}

bool Color::operator<(Color const& color) const {
    if(compareTo(color) < 0)
        return true;
    return false;
}

bool Color::operator>(Color const& color) const {
    if(compareTo(color) > 0)
        return true;
    return false;
}

bool Color::operator<=(Color const& color) const {
    if(compareTo(color) <= 0)
        return true;
    return false;
}

bool Color::operator>=(Color const& color) const {
    if(compareTo(color) >= 0)
        return true;
    return false;
}

PInt Color::compareTo(Color const& color) const {
    if(_red != color._red)
    {
        if(_red < color._red) return -1;
        else return 1;
    }
    else if(_green != color._green)
    {
        if(_green < color._green) return -1;
        else return 1;
    }
    else if(_blue != color._blue)
    {
        if(_blue < color._blue) return -1;
        else return 1;
    }
    else if(_alpha != color._alpha)
    {
        if(_alpha < color._alpha) return -1;
        else return 1;
    }
    
    return 0;
}

Color::RGB Color::hsvToRgb(PInt hue, PInt saturation, PInt value) {
    PFloat h = hue / 255.0 * 360.0;
    PFloat s = saturation / 255.0;
    PFloat v = value / 255.0;
        
    PFloat r, g, b; r=g=b=0;
    
    if (s == 0) {
        r = g = b = v;
    } else {
        PInt hi = floorf(h / 60.0);
        PFloat f = (h / 60.0) - hi;
        PFloat p = v * (1.0 - s);
        PFloat q = v * (1.0 - f * s);
        PFloat t = v * (1.0 - (1.0 - f) * s);
        
        switch (hi) {
            case 0: {
                r = v; g = t; b = p; 
            } break;                
            case 1: {
                r = q; g = v; b = p;
            } break;                
            case 2: {
                r = p; g = v; p = t;
            } break;                
            case 3: {
                r = p; g = q; p = v;
            } break;                
            case 4: {
                r = t; g = p; p = v;
            } break;                
            default:
            case 5: {
                r = v; g = p; p = q;
            } break;
        }
    }
    
    Color::RGB rgb;
    rgb.red = (PInt)pRound(r * 255.0); 
    rgb.green = (PInt)pRound(g * 255.0); 
    rgb.blue = (PInt)pRound(b * 255.0); 
    
    return rgb;
}

Color::HSV Color::rgbToHsv(PInt red, PInt green, PInt blue) {
    PFloat r = red / 255.0;
    PFloat g = green / 255.0;
    PFloat b = blue / 255.0;
    PFloat max = PRISM_MAX3(r, g, b);
    PFloat min = PRISM_MIN3(r, g, b);
        
    PFloat h, s, v; h = s = v = 0;
    
    if (r == max) {
        h = (0 + (g-b)/(max-min)) * 60;
    } else if (g == max) {
        h = (2 + (b-r)/(max-min)) * 60;
    } else {
        h = (4 + (r-g)/(max-min)) * 60;
    }
    
    s = (max - min) / max;
    v = max;
    
    Color::HSV hsv;
    hsv.hue = (PInt)pRound((modulus(h, 360.0)) / 360.0 * 255.0);
    hsv.saturation = (PInt)pRound(s * 255.0);
    hsv.value = (PInt)pRound(v * 255.0);

    return hsv;
}

String Color::toString() {
    return String::fromFormat("[Color red='%d' green='%d' blue='%d']", _red, _green, _blue);
}


/////////////////////
// Color Constants //
/////////////////////

const Color Color::AliceBlue            (240,248,255      );	 // (0xF0, 0xF8, 0xFF);
const Color Color::AntiqueWhite         (250,235,215      );	 // (0xFA, 0xEB, 0xD7);
const Color Color::Aqua                 (  0,255,255      );	 // (0x00, 0xFF, 0xFF);
const Color Color::Aquamarine           (127,255,212      );	 // (0x7F, 0xFF, 0xD4);
const Color Color::Azure                (240,255,255      );	 // (0xF0, 0xFF, 0xFF);

const Color Color::Beige                (245,245,220      );	 // (0xF5, 0xF5, 0xDC);
const Color Color::Bisque               (255,228,196      );	 // (0xFF, 0xE4, 0xC4);
const Color Color::Black                (  0,  0,  0      );	 // (0x00, 0x00, 0x00);
const Color Color::BlanchedAlmond       (255,255,205      );	 // (0xFF, 0xEB, 0xCD);
const Color Color::Blue                 (  0,  0,255      );	 // (0x00, 0x00, 0xFF);
const Color Color::BlueViolet           (138, 43,226      );	 // (0x8A, 0x2B, 0xE2);
const Color Color::Brown                (165, 42, 42      );	 // (0xA5, 0x2A, 0x2A);
const Color Color::Burlywood            (222,184,135      );	 // (0xDE, 0xB8, 0x87);

const Color Color::CadetBlue            ( 95,158,160      );	 // (0x5F, 0x9E, 0xA0);
const Color Color::Chartreuse           (127,255,  0      );	 // (0x7F, 0xFF, 0x00);
const Color Color::Chocolate            (210,105, 30      );	 // (0xD2, 0x69, 0x1E);
const Color Color::Coral                (255,127, 80      );	 // (0xFF, 0x7F, 0x50);
const Color Color::CornflowerBlue       (100,149,237      );	 // (0x64, 0x95, 0xED);
const Color Color::Cornsilk             (255,248,220      );	 // (0xFF, 0xF8, 0xDC);
const Color Color::Crimson              (220, 20, 60      );	 // (0xDC, 0x14, 0x3C);
const Color Color::Cyan                 (  0,255,255      );	 // (0x00, 0xFF, 0xFF);

const Color Color::DarkBlue             (  0,  0,139      );	 // (0x00, 0x00, 0x8B);
const Color Color::DarkCyan             (  0,139,139      );	 // (0x00, 0x8B, 0x8B);
const Color Color::DarkGoldenRod        (184,134, 11      );	 // (0xB8, 0x86, 0x0B);
const Color Color::DarkGray             (169,169,169      );	 // (0xA9, 0xA9, 0xA9);
const Color Color::DarkGreen            (  0,100,  0      );	 // (0x00, 0x64, 0x00);
const Color Color::DarkKhaki            (189,183,107      );	 // (0xBD, 0xB7, 0x6B);
const Color Color::DarkMagenta          (139,  0,139      );	 // (0x8B, 0x00, 0x8B);
const Color Color::DarkOliveGreen       ( 85,107, 47      );	 // (0x55, 0x6B, 0x2F);
const Color Color::DarkOrange           (255,140,  0      );	 // (0xFF, 0x8C, 0x00);
const Color Color::DarkOrchid           (153, 50,204      );	 // (0x99, 0x32, 0xCC);
const Color Color::DarkRed              (139,  0,  0      );	 // (0x8B, 0x00, 0x00);
const Color Color::DarkSalmon           (233,150,122      );	 // (0xE9, 0x96, 0x7A);
const Color Color::DarkSeaGreen         (143,188,143      );	 // (0x8F, 0xBC, 0x8B);
const Color Color::DarkSlateBlue        ( 72, 61,139      );	 // (0x48, 0x3D, 0x8B);
const Color Color::DarkSlateGray        ( 47, 79, 79      );	 // (0x2F, 0x4F, 0x4F);
const Color Color::DarkTurquoise        (  0,206,209      );	 // (0x00, 0xCE, 0xD1);
const Color Color::DarkViolet           (148,  0,211      );	 // (0x94, 0x00, 0xD3);
const Color Color::DeepPink             (255, 20,147      );	 // (0xFF, 0x14, 0x93);
const Color Color::DeepSkyBlue          (  0,191,255      );	 // (0x00, 0xBF, 0xFF);
const Color Color::DimGray              (105,105,105      );	 // (0x69, 0x69, 0x69);
const Color Color::DodgerBlue           ( 30,144,255      );	 // (0x1E, 0x90, 0xFF);

const Color Color::FireBrick            (178, 34, 34      );	 // (0xB2, 0x22, 0x22);
const Color Color::FloralWhite          (255,250,240      );	 // (0xFF, 0xFA, 0xF0);
const Color Color::ForestGreen          ( 34,139, 34      );	 // (0x22, 0x8B, 0x22);
const Color Color::Fuchsia              (255,  0,255      );	 // (0xFF, 0x00, 0xFF);

const Color Color::Gainsboro            (220,220,220      );	 // (0xDC, 0xDC, 0xDC);
const Color Color::GhostWhite           (248,248,255      );	 // (0xF8, 0xF8, 0xFF);
const Color Color::Gold                 (255,215,  0      );	 // (0xFF, 0xD7, 0x00);
const Color Color::GoldenRod            (218,165, 32      );	 // (0xDA, 0xA5, 0x20);
const Color Color::Gray                 (127,127,127      );	 // (0x80, 0x80, 0x80);
const Color Color::Green                (  0,128,  0      );	 // (0x00, 0x80, 0x00);
const Color Color::GreenYellow          (173,255, 47      );	 // (0xAD, 0xFF, 0x2F);

const Color Color::HoneyDew             (240,255,240      );	 // (0xF0, 0xFF, 0xF0);
const Color Color::HotPink              (255,105,180      );	 // (0xFF, 0x69, 0xB4);

const Color Color::IndianRed            (205, 92, 92      );	 // (0xCD, 0x5C, 0x5C);
const Color Color::Indigo               ( 75,  0,130      );	 // (0x4B, 0x00, 0x82);
const Color Color::Ivory                (255,255,240      );	 // (0xFF, 0xFF, 0xF0);

const Color Color::Khaki                (240,230,140      );	 // (0xF0, 0xE6, 0x8C);

const Color Color::Lavender             (230,230,250      );	 // (0xE6, 0xE6, 0xFA);
const Color Color::LavenderBlush        (255,240,245      );	 // (0xFF, 0xF0, 0xF5);
const Color Color::Lawngreen            (124,252,  0      );	 // (0x7C, 0xFC, 0x00);
const Color Color::LemonChiffon         (255,250,205      );	 // (0xFF, 0xFA, 0xCD);
const Color Color::LightBlue            (173,216,230      );	 // (0xAD, 0xD8, 0xE6);
const Color Color::LightCoral           (240,128,128      );	 // (0xF0, 0x80, 0x80);
const Color Color::LightCyan            (224,255,255      );	 // (0xE0, 0xFF, 0xFF);
const Color Color::LightGoldenRodYellow (250,250,210      );	 // (0xFA, 0xFA, 0xD2);
const Color Color::LightGreen           (144,238,144      );	 // (0x90, 0xEE, 0x90);
const Color Color::LightGray            (211,211,211      );	 // (0xD3, 0xD3, 0xD3);
const Color Color::LightPink            (255,182,193      );	 // (0xFF, 0xB6, 0xC1);
const Color Color::LightSalmon          (255,160,122      );	 // (0xFF, 0xA0, 0x7A);
const Color Color::LightSeaGreen        ( 32,178,170      );	 // (0x20, 0xB2, 0xAA);
const Color Color::LightSkyBlue         (135,206,250      );	 // (0x87, 0xCE, 0xFA);
const Color Color::LightSlateGray       (119,136,153      );	 // (0x77, 0x88, 0x99);
const Color Color::LightSteelBlue       (176,196,222      );	 // (0xB0, 0xC4, 0xDE);
const Color Color::LightYellow          (255,255,224      );	 // (0xFF, 0xFF, 0xE0);
const Color Color::Lime                 (  0,255,  0      );	 // (0x00, 0xFF, 0x00);
const Color Color::LimeGreen            ( 50,205, 50      );	 // (0x32, 0xCD, 0x32);
const Color Color::Linen                (250,240,230      );	 // (0xFA, 0xF0, 0xE6);

const Color Color::Magenta              (255,  0,255      );	 // (0xFF, 0x00, 0xFF);
const Color Color::Maroon               (128,  0,  0      );	 // (0x80, 0x00, 0x00);
const Color Color::MediumAquamarine     (102,205,170      );	 // (0x66, 0xCD, 0xAA);
const Color Color::MediumBlue           (  0,  0,205      );	 // (0x00, 0x00, 0xCD);
const Color Color::MediumOrchid         (186, 85,211      );	 // (0xBA, 0x55, 0xD3);
const Color Color::MediumPurple         (147,112,219      );	 // (0x93, 0x70, 0xDB);
const Color Color::MediumSeaGreen       ( 60,179,113      );	 // (0x3C, 0xB3, 0x71);
const Color Color::MediumSlateBlue      (123,104,238      );	 // (0x7B, 0x68, 0xEE);
const Color Color::MediumSpringGreen    (  0,250,154      );	 // (0x00, 0xFA, 0x9A);
const Color Color::MediumTurquoise      ( 72,209,204      );	 // (0x48, 0xD1, 0xCC);
const Color Color::MediumVioletRed      (199, 21,133      );	 // (0xC7, 0x15, 0x85);
const Color Color::MidnightBlue         ( 25, 25,112      );	 // (0x19, 0x19, 0x70);
const Color Color::MintCream            (245,255,250      );	 // (0xF5, 0xFF, 0xFA);
const Color Color::MistyRose            (255,228,225      );	 // (0xFF, 0xE4, 0xE1);
const Color Color::Moccasin             (255,228,181      );	 // (0xFF, 0xE4, 0xB5);

const Color Color::NavajoWhite          (255,222,173      );	 // (0xFF, 0xDE, 0xAD);
const Color Color::Navy                 (  0,  0,128      );	 // (0x00, 0x00, 0x80);
const Color Color::Navyblue             (159,175,223      );	 // (0x9F, 0xAF, 0xDF);

const Color Color::OldLace              (253,245,230      );	 // (0xFD, 0xF5, 0xE6);
const Color Color::Olive                (128,128,  0      );	 // (0x80, 0x80, 0x00);
const Color Color::OliveDrab            (107,142, 35      );	 // (0x6B, 0x8E, 0x23);
const Color Color::Orange               (255,165,  0      );	 // (0xFF, 0xA5, 0x00);
const Color Color::OrangeRed            (255, 69,  0      );	 // (0xFF, 0x45, 0x00);
const Color Color::Orchid               (218,112,214      );	 // (0xDA, 0x70, 0xD6);

const Color Color::PaleGoldenRod        (238,232,170      );	 // (0xEE, 0xE8, 0xAA);
const Color Color::PaleGreen            (152,251,152      );	 // (0x98, 0xFB, 0x98);
const Color Color::PaleTurquoise        (175,238,238      );	 // (0xAF, 0xEE, 0xEE);
const Color Color::PaleVioletRed        (219,112,147      );	 // (0xDB, 0x70, 0x93);
const Color Color::PapayaWhip           (255,239,213      );	 // (0xFF, 0xEF, 0xD5);
const Color Color::PeachPuff            (255,218,185      );	 // (0xFF, 0xDA, 0xB9);
const Color Color::Peru                 (205,133, 63      );	 // (0xCD, 0x85, 0x3F);
const Color Color::Pink                 (255,192,203      );	 // (0xFF, 0xC0, 0xCB);
const Color Color::Plum                 (221,160,221      );	 // (0xDD, 0xA0, 0xDD);
const Color Color::PowderBlue           (176,224,230      );	 // (0xB0, 0xE0, 0xE6);
const Color Color::Purple               (128,  0,128      );	 // (0x80, 0x00, 0x80);

const Color Color::Red                  (255,  0,  0      );	 // (0xFF, 0x00, 0x00);
const Color Color::RosyBrown            (188,143,143      );	 // (0xBC, 0x8F, 0x8F);
const Color Color::RoyalBlue            ( 65,105,225      );	 // (0x41, 0x69, 0xE1);

const Color Color::SaddleBrown          (139, 69, 19      );	 // (0x8B, 0x45, 0x13);
const Color Color::Salmon               (250,128,114      );	 // (0xFA, 0x80, 0x72);
const Color Color::SandyBrown           (244,164, 96      );	 // (0xF4, 0xA4, 0x60);
const Color Color::SeaGreen             ( 46,139, 87      );	 // (0x2E, 0x8B, 0x57);
const Color Color::SeaShell             (255,245,238      );	 // (0xFF, 0xF5, 0xEE);
const Color Color::Sienna               (160, 82, 45      );	 // (0xA0, 0x52, 0x2D);
const Color Color::Silver               (192,192,192      );	 // (0xC0, 0xC0, 0xC0);
const Color Color::SkyBlue              (135,206,235      );	 // (0x87, 0xCE, 0xEB);
const Color Color::SlateBlue            (106, 90,205      );	 // (0x6A, 0x5A, 0xCD);
const Color Color::SlateGray            (112,128,144      );	 // (0x70, 0x80, 0x90);
const Color Color::Snow                 (255,250,250      );	 // (0xFF, 0xFA, 0xFA);
const Color Color::SpringGreen          (  0,255,127      );	 // (0x00, 0xFF, 0x7F);
const Color Color::SteelBlue            ( 70,130,180      );	 // (0x46, 0x82, 0xB4);

const Color Color::Tan                  (210,180,140      );	 // (0xD2, 0xB4, 0x8C);
const Color Color::Teal                 (  0,128,128      );	 // (0x00, 0x80, 0x80);
const Color Color::Thistle              (216,191,216      );	 // (0xD8, 0xBF, 0xD8);
const Color Color::Tomato               (255, 99, 71      );	 // (0xFF, 0x63, 0x47);
const Color Color::Turquoise            ( 64,224,208      );	 // (0x40, 0xE0, 0xD0);
const Color Color::Transparent          (255,255,255,0    );	 // (0xFF, 0xFF, 0xFF);

const Color Color::Violet               (238,130,238      );	 // (0xEE, 0x82, 0xEE);

const Color Color::Wheat                (245,222,179      );	 // (0xF5, 0xDE, 0xB3);
const Color Color::White                (255,255,255      );	 // (0xFF, 0xFF, 0xFF);
const Color Color::WhiteSmoke           (245,245,245      );	 // (0xF5, 0xF5, 0xF5);

const Color Color::Yellow               (255,255,  0      );	 // (0xFF, 0xFF, 0x00);
const Color Color::YellowGreen          (139,205, 50      );	 // (0x9A, 0xCD, 0x32);

const Color Color::Gray0                (0.0f, 0.0f, 0.0f, 1.0f     );
const Color Color::Gray10               (0.1f, 0.1f, 0.1f, 1.0f     );
const Color Color::Gray20               (0.2f, 0.2f, 0.2f, 1.0f     );
const Color Color::Gray30               (0.3f, 0.3f, 0.3f, 1.0f     );
const Color Color::Gray40               (0.4f, 0.4f, 0.4f, 1.0f     );
const Color Color::Gray50               (0.5f, 0.5f, 0.5f, 1.0f     );
const Color Color::Gray60               (0.6f, 0.6f, 0.6f, 1.0f     );
const Color Color::Gray70               (0.7f, 0.7f, 0.7f, 1.0f     );
const Color Color::Gray80               (0.8f, 0.8f, 0.8f, 1.0f     );
const Color Color::Gray90               (0.9f, 0.9f, 0.9f, 1.0f     );
const Color Color::Gray100              (1.0f, 1.0f, 1.0f, 1.0f     );